package ar.uba.fi.RSACryptoFiuba.view.keygeneration;

import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;

import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.LayoutStyle.ComponentPlacement;

import ar.uba.fi.RSACryptoFiuba.model.RSAAlgorithmLearningStrategy;
import ar.uba.fi.RSACryptoFiuba.model.keygeneration.RSAKeyGenerationAlgorithm;
import ar.uba.fi.RSACryptoFiuba.model.keygeneration.RSAKeyGenerationMessage;
import ar.uba.fi.RSACryptoFiuba.view.commons.ButtonsPanel;
import ar.uba.fi.RSACryptoFiuba.view.commons.LearningInteractionPanel;
import ar.uba.fi.RSACryptoFiuba.view.commons.ResultsPanel;


@SuppressWarnings("serial")
public class GenerateKeysLearningPanel extends JPanel implements Observer {
	
	private GenerateKeysProgressPanel progressPanel;
	    
    private JPanel cards;
    private CardLayout cardsLayout;
    private LearningInteractionPanel generatePPanel;
    private LearningInteractionPanel generateQPanel;
    private LearningInteractionPanel calculateNPanel;
    private LearningInteractionPanel calculatePhiPanel;
    private LearningInteractionPanel calculateDPanel;
    private LearningInteractionPanel calculateEPanel;
    private ResultsPanel generateKeysResultsPanel;
    
    private JSeparator lineSeparator;
    private ButtonsPanel buttonsPanel;
    
    private RSAKeyGenerationAlgorithm algorithm;
    private RSAKeyGenerationMessage message;
    	
	public GenerateKeysLearningPanel(ActionListener modeActionListener, RSAKeyGenerationMessage message){
		this.message = message;
		this.algorithm = new RSAKeyGenerationAlgorithm(new RSAAlgorithmLearningStrategy(), this.message);
		this.algorithm.getMessage().addObserver(this);
		initComponentes(modeActionListener);
		algorithm.executeStep();
		handleButtonsEnable();
	}
	
	private void initComponentes(ActionListener modeButtonActionListener){		
		
		//Progress panel
        progressPanel = new GenerateKeysProgressPanel();
                        
        //Step Panels
        cards = new JPanel();
        cards.setPreferredSize(new Dimension(670, 165));
        
        cardsLayout = new CardLayout();
        cards.setLayout(cardsLayout);
        
        generatePPanel = new LearningInteractionPanel("p: número primo", "<html><p >Se genera aleatoriamente un número</p><p>natural p distinto de 0 que sea primo.</p><p>Un número es primo si sólo es divisible</p><p>por 1 y por sí mismo.</p></html>");
        cards.add(generatePPanel, "GENERATE_P");
        generateQPanel = new LearningInteractionPanel("q: número primo distinto de p", "<html><p>Se genera aleatoriamente un número</p><p>natural q distinto de 0 que sea</p><p>primo. Este número debe ser además</p><p>distinto de p.</p></html>");
        cards.add(generateQPanel, "GENERATE_Q");
        calculateNPanel = new LearningInteractionPanel("n = p * q", "<html><p>Se calcula n como el producto de p y q.</p><p>Este número formará parte tanto de la</p><p>clave pública como de la privada. La</p><p>cantidad de dígitos de n determinará la</p><p>cantidad de pasos que se harán para</p><p>encriptar y desencriptar.</p></html>");
        cards.add(calculateNPanel, "CALCULATE_N");
        calculatePhiPanel = new LearningInteractionPanel("phi = (p-1) * (q-1)", "<html><p>Se calcula phi como el producto de p menos 1</p><p>y q menos 1. Este número servirá para calcular</p><p>las componentes e y d de las claves privada</p><p>y pública respectivamente.</p></html>");
        cards.add(calculatePhiPanel, "CALCULATE_PHI");
        calculateDPanel = new LearningInteractionPanel("d : mcd(d, phi) = 1", "<html><p>Se calcula d que es un número</p><p>relativamente primo (coprimo) de phi.</p><p>Este número formará parte de</p><p>la clave privada.</p></html>");
        cards.add(calculateDPanel, "CALCULATE_D");
        calculateEPanel = new LearningInteractionPanel("e : (e * d) mod phi = 1", "<html><p>Se calcula e que es el número</p><p>inverso multiplicativo de d.</p><p>Este número formará parte de</p><p>la clave pública.</p></html>");
        cards.add(calculateEPanel, "CALCULATE_D");
        generateKeysResultsPanel = new ResultsPanel();
        cards.add(generateKeysResultsPanel, "GENERATE_RESULTS");
	        
		//Buttons Panel
        lineSeparator = new JSeparator();        

		ActionListener firstButtonActionListener = new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if(!algorithm.getCurrentStep().isFirst()){
	    			algorithm.reset();
	    			algorithm.executeStep();
        			cardsLayout.first(cards);
        		}
				handleButtonsEnable();
			}
		};  
	    
		ActionListener previousButtonActionListener = new ActionListener() {
	    	public void actionPerformed(ActionEvent e) {
	    		if(!algorithm.getCurrentStep().isFirst()){
	    			algorithm.undoStep();
        			algorithm.movePreviousStep();
        			cardsLayout.previous(cards);
        		}
	    		handleButtonsEnable();
	    	}
	    };
       
        ActionListener nextButtonActionListener = new ActionListener() {
        	public void actionPerformed(ActionEvent e) {
        		if(!algorithm.getCurrentStep().isLast()){
        			algorithm.moveNextStep();
        			algorithm.executeStep();
        			cardsLayout.next(cards);
        		}
        		handleButtonsEnable();
        	}	
        };
        
        ActionListener lastButtonActionListener = new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if(!algorithm.getCurrentStep().isLast()){
        			algorithm.resolve();
        			cardsLayout.last(cards);	
        		}
				handleButtonsEnable();
			}
		};
	  
		buttonsPanel = new ButtonsPanel(modeButtonActionListener, firstButtonActionListener, previousButtonActionListener, nextButtonActionListener, lastButtonActionListener);

		//--------------------------------      
      
		GroupLayout generateKeysTabLayout = new GroupLayout(this);
        this.setLayout(generateKeysTabLayout);
        generateKeysTabLayout.setHorizontalGroup(
            generateKeysTabLayout.createParallelGroup(Alignment.LEADING)
            .addGroup(generateKeysTabLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(generateKeysTabLayout.createParallelGroup(Alignment.LEADING)
                    .addComponent(lineSeparator, Alignment.TRAILING)
                    .addComponent(progressPanel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(cards, GroupLayout.DEFAULT_SIZE, 830, Short.MAX_VALUE)
                    .addComponent(buttonsPanel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addContainerGap())
        );
        generateKeysTabLayout.setVerticalGroup(
            generateKeysTabLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(generateKeysTabLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(progressPanel, GroupLayout.PREFERRED_SIZE, 81, GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(ComponentPlacement.RELATED)
                .addComponent(cards, GroupLayout.DEFAULT_SIZE, 159, Short.MAX_VALUE)
                .addPreferredGap(ComponentPlacement.RELATED)
                .addComponent(lineSeparator, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(ComponentPlacement.UNRELATED)
                .addComponent(buttonsPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                .addGap(6, 6, 6))
        );
	}

	public void handleButtonsEnable(){
		this.buttonsPanel.getFirstButton().setEnabled(!algorithm.getCurrentStep().isFirst());
		this.buttonsPanel.getPreviousButton().setEnabled(!algorithm.getCurrentStep().isFirst());
		this.buttonsPanel.getNextButton().setEnabled(!algorithm.getCurrentStep().isLast());
		this.buttonsPanel.getLastButton().setEnabled(!algorithm.getCurrentStep().isLast());
	}

	public void update(Observable o, Object arg) {
		RSAKeyGenerationMessage message = (RSAKeyGenerationMessage) o;
		
		this.progressPanel.setPValue(message.getP());
		this.progressPanel.setQValue(message.getQ());
		this.progressPanel.setNValue(message.getN());
		this.progressPanel.setPhiValue(message.getPhi());
		this.progressPanel.setDValue(message.getD());
		this.progressPanel.setEValue(message.getE());
		
		generatePPanel.setOperationValue(message.getP());
        generateQPanel.setOperationValue(message.getQ());
        calculateNPanel.setOperationValue(message.getN());
        calculatePhiPanel.setOperationValue(message.getPhi());
        calculateDPanel.setOperationValue(message.getD());
        calculateEPanel.setOperationValue(message.getE());
        
        generateKeysResultsPanel.setResultsValues("Clave Pública = (e, n)", "Clave Privada = (d, n)", String.format("(%s, %s)", message.getE(), message.getN()), String.format("(%s, %s)", message.getD(), message.getN()));
	}
		
}	

